package com.rule.rule.engine;

import com.rule.knowledge.base.models.Rule;
import com.rule.lang.parser.RuleParser;
import com.rule.rest.RuleNamespace;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
/**
 * 推理机
 * 推理机是规则引擎的核心部分。它主要分三步对输入数据执行规则
 * 匹配：将事实/条件和数据与规则集进行匹配。它返回一组满足的规则。
 * 解析（RESOLVE）：解决冲突的规则集，给出选中的一条规则。
 * 执行（EXECUTE）：对给定数据运行所选规则的操作并返回结果输出
 */
@Slf4j
@Service
public abstract class InferenceEngine<INPUT_DATA, OUTPUT_RESULT> {

    @Autowired
    private RuleParser<INPUT_DATA, OUTPUT_RESULT> ruleParser;

    /**
     * 对给定数据的规则集运行推理引擎
     * @param listOfRules
     * @param inputData
     * @return
     */
    public OUTPUT_RESULT run (List<Rule> listOfRules, INPUT_DATA inputData){
        if (null == listOfRules || listOfRules.isEmpty()){
            return null;
        }

        //STEP 1 匹配(MATCH) : 将事实和数据与规则集相匹配
        List<Rule> conflictSet = match(listOfRules, inputData);

        //STEP 2 解析(RESOLVE) : 解决冲突并给出选定的一条规则.
        Rule resolvedRule = resolve(conflictSet);
        if (null == resolvedRule){
            return null;
        }

        //STEP 3 执行(EXECUTE) : 对给定数据运行所选规则的操作并返回输出
        OUTPUT_RESULT outputResult = executeRule(resolvedRule, inputData);

        return outputResult;
    }

    /**
     *我们可以在这里使用任何模式匹配算法:
     * 1. Rete
     * 2. Linear
     * 3. Treat
     * 4. Leaps
     *
     * 这里我们使用线性匹配算法进行模式匹配. Linear
     * @param listOfRules
     * @param inputData
     * @return
     */
    protected List<Rule> match(List<Rule> listOfRules, INPUT_DATA inputData){
        return listOfRules.stream()
                .filter(
                        rule -> {
                            String condition = rule.getCondition();
                            return ruleParser.parseCondition(condition, inputData);
                        }
                )
                .collect(Collectors.toList());
    }

    /**
     * 我们可以在这里使用任何解析技术:
     * 1. Lex
     * 2. Recency
     * 3. MEA
     * 4. Refactor
     * 5. Priority wise
     * 6. Spring SPEL
     *
     *  这里我们使用的是先查找规则逻辑.
     * @param conflictSet
     * @return
     */
    protected Rule resolve(List<Rule> conflictSet){
        Optional<Rule> rule = conflictSet.stream()
                .findFirst();
        if (rule.isPresent()){
            return rule.get();
        }
        return null;
    }

    /**
     * 对输入数据执行所选规则
     * @param rule 规则
     * @param inputData 输入数据
     * @return
     */
    protected OUTPUT_RESULT executeRule(Rule rule, INPUT_DATA inputData){
        OUTPUT_RESULT outputResult = initializeOutputResult();
        return ruleParser.parseAction(rule.getAction(), inputData, outputResult);
    }
    /**
     * 在扩展的特定领域推理引擎中实现
     */
    protected abstract OUTPUT_RESULT initializeOutputResult();
    /**
     * 在扩展的特定领域推理引擎中实现
     */
    protected abstract RuleNamespace getRuleNamespace();
}
